/*
 * gray_matrix_cl.h
 *
 *  Created on: 2016年2月25日
 *      Author: guyadong
 */

#ifndef FACEDETECT_GRAY_MATRIX_CL_H_
#define FACEDETECT_GRAY_MATRIX_CL_H_
#include "mycl.h"
#include "detect_cl_types.h"
#include "facecl_context.h"
#include "assert_macros.h"
#include "memory_cl.h"

namespace gdface {
/* 灰度图像矩阵
 * 继承自 matrix_cl，为灰度图像矩阵类
 * */
struct gray_matrix_cl:public matrix_cl<cl_uchar,cl::Image2D>{
	using base_type=matrix_cl<cl_uchar,cl::Image2D>;
	using matrix_cl_type=matrix_cl<cl_uchar,cl::Buffer>;

	gray_matrix_cl(size_t width, size_t height, int align=0, const cl_uchar*ptr=nullptr,
		cl_mem_flags flags = CL_MEM_READ_WRITE, const facecl_context &context = global_facecl_context);
	template<typename _V,typename ENABLE=typename std::enable_if<std::is_base_of<std::vector<cl_uchar>,_V>::value>::type>
	gray_matrix_cl(size_t width, size_t height, int align, _V&&in,
		cl_mem_flags flags = CL_MEM_READ_WRITE,
		const facecl_context &context = global_facecl_context)
		:matrix_cl(width,height,align,std::forward<_V>(in)){
		this->cl_mem_obj = createImage2DGray(flags, context.getContext());
	}

	gray_matrix_cl(const image_matrix_v& img, cl_mem_flags flags = CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
			const facecl_context& context = global_facecl_context);
	gray_matrix_cl(image_matrix_v&& img, cl_mem_flags flags = CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
			const facecl_context& context = global_facecl_context);
	gray_matrix_cl(const image_matrix& img, cl_mem_flags flags = CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
			const facecl_context& context = global_facecl_context);

	/*
	 * 为matrix_cl_type定制的构造函数
	 * 可根据img参数是否为右值引用决定成员变量v(std::vector)的构造方式 
	 * */
	template<typename _M,bool _RV=std::is_rvalue_reference<_M&&>::value	>
	gray_matrix_cl (_M &&img, typename std::enable_if<std::is_base_of<matrix_cl_type,typename std::decay<_M>::type>::value,cl_mem_flags>::type flags=CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR,const facecl_context &context = global_facecl_context){
		this->width=img.width;
		this->height=img.height;
		this->row_stride=img.row_stride;
		this->col_stride=img.col_stride;
		this->v=_RV?std::move(img.v):img.v;
		this->cl_mem_obj=createImage2DGray(flags,context.getContext());
	}
	gray_matrix_cl()=default;
	gray_matrix_cl(const gray_matrix_cl&)=default;
	gray_matrix_cl(gray_matrix_cl&&)=default;
	gray_matrix_cl& operator=(const gray_matrix_cl&)=default;
	gray_matrix_cl& operator=(gray_matrix_cl&&)=default;
	gray_matrix_cl zoom(size_t dst_width, size_t dst_height, const facecl_context& context = global_facecl_context,bool download=false)const;
	inline gray_matrix_cl zoom(float factor_w, float factor_h,const facecl_context& context = global_facecl_context, bool download = false)const{
		return zoom((size_t)(width*factor_w),(size_t)(height*factor_h),context, download);
	}
	inline gray_matrix_cl zoom(float factor,const facecl_context& context = global_facecl_context,bool download=false)const{
		return zoom(factor,factor,context,download);
	}
	inline gray_matrix_cl zoom_w(size_t dst_width,const facecl_context& context = global_facecl_context,bool download=false)const{
		return zoom(dst_width,height*dst_width/width,context,download);
	}
	inline gray_matrix_cl zoom_h(size_t dst_height,const facecl_context& context = global_facecl_context,bool download=false)const{
		return zoom(width*dst_height/height,dst_height,context,download);
	}

	matrix_cl_type to_matrix_cl() const;
	/*
	 * 计算图像的积分图/积方图，
	 * 返回积分图矩阵对象
	 * DST_E 积分图矩阵的的元素类型(cl_ulong,cl_float,cl_double)
	 * ALIGN 积分图行对齐指数
	 * integral_type为积分图类型
	 * INTEG_DEFAULT 积分图
	 * INTEG_SQUARE 积方图
	 */
	template<typename DST_E,int ALIGN=1,integral_type INTEG_TYPE,typename RET_TYPE=matrix_cl<DST_E,cl::Buffer>>
	typename std::enable_if<std::is_same<cl_float,DST_E>::value&&integ_count!=INTEG_TYPE,RET_TYPE>::type
	integral() const {
		if(tls::device_type_is(CL_DEVICE_TYPE_CPU)){
			return to_matrix_cl().integral<DST_E>(
					 integ_default==INTEG_TYPE?
							 KERNEL_NAME_VAR(prefix_sum_col_and_transpose,PREFIX_SUM_SUFFIX(_uchar_float,integ_default))
							:KERNEL_NAME_VAR(prefix_sum_col_and_transpose,PREFIX_SUM_SUFFIX(_uchar_float,integ_square))
					 ,KERNEL_NAME_VAR(prefix_sum_col_and_transpose,PREFIX_SUM_SUFFIX(_float_float,integ_default))
					 ,ALIGN);
		}
		return to_matrix_cl().integral<DST_E>(
				integ_default==INTEG_TYPE?
									 KERNEL_NAME_VAR(integral_block,PREFIX_SUM_SUFFIX(_uchar_float,integ_default))
									:KERNEL_NAME_VAR(integral_block,PREFIX_SUM_SUFFIX(_uchar_float,integ_square))
				,KERNEL_NAME_VAR(integral_scan_v,_float)
				,KERNEL_NAME_VAR(integral_combine_v,_float)
				,KERNEL_NAME_VAR(integral_scan_h,_float)
				,KERNEL_NAME_VAR(integral_combine_h,_float)
				,ALIGN);
	}
	template<typename DST_E,int ALIGN=1,integral_type INTEG_TYPE,typename RET_TYPE=matrix_cl<DST_E,cl::Buffer>>
	typename std::enable_if<std::is_same<cl_double,DST_E>::value&&integ_count!=INTEG_TYPE,RET_TYPE>::type
	integral() const {
		if(tls::device_type_is(CL_DEVICE_TYPE_CPU)){
			return to_matrix_cl().integral<DST_E>(
					integ_default==INTEG_TYPE?
					 	  KERNEL_NAME_VAR(prefix_sum_col_and_transpose,PREFIX_SUM_SUFFIX(_uchar_double,integ_default))
						 :KERNEL_NAME_VAR(prefix_sum_col_and_transpose,PREFIX_SUM_SUFFIX(_uchar_double,integ_square))
					 ,KERNEL_NAME_VAR(prefix_sum_col_and_transpose,PREFIX_SUM_SUFFIX(_double_double,integ_default))
					 ,ALIGN);
		}
		return to_matrix_cl().integral<DST_E>(integ_default==INTEG_TYPE?
									 KERNEL_NAME_VAR(integral_block,PREFIX_SUM_SUFFIX(_uchar_double,integ_default))
									:KERNEL_NAME_VAR(integral_block,PREFIX_SUM_SUFFIX(_uchar_double,integ_square))
				 ,KERNEL_NAME_VAR(integral_scan_v,_double)
				 ,KERNEL_NAME_VAR(integral_combine_v,_double)
				 ,KERNEL_NAME_VAR(integral_scan_h,_double)
				 ,KERNEL_NAME_VAR(integral_combine_h,_double)
				 ,ALIGN);
	}
	template<typename DST_E,int ALIGN=1,integral_type INTEG_TYPE,typename RET_TYPE=matrix_cl<DST_E,cl::Buffer>>
	typename std::enable_if<std::is_same<cl_ulong,DST_E>::value&&integ_count!=INTEG_TYPE,RET_TYPE>::type
	integral() const {
		if(tls::device_type_is(CL_DEVICE_TYPE_CPU)){
			return to_matrix_cl().integral<DST_E>(
					integ_default==INTEG_TYPE?
							KERNEL_NAME_VAR(prefix_sum_col_and_transpose,PREFIX_SUM_SUFFIX(_uchar_ulong,integ_default))
							:KERNEL_NAME_VAR(prefix_sum_col_and_transpose,PREFIX_SUM_SUFFIX(_uchar_ulong,integ_square))
					 ,KERNEL_NAME_VAR(prefix_sum_col_and_transpose,PREFIX_SUM_SUFFIX(_ulong_ulong,integ_default))
					 ,ALIGN);
		}
		return to_matrix_cl().integral<DST_E>(
				integ_default==INTEG_TYPE?
									 KERNEL_NAME_VAR(integral_block,PREFIX_SUM_SUFFIX(_uchar_ulong,integ_default))
									:KERNEL_NAME_VAR(integral_block,PREFIX_SUM_SUFFIX(_uchar_ulong,integ_square))
				,KERNEL_NAME_VAR(integral_scan_v,_ulong)
				,KERNEL_NAME_VAR(integral_combine_v,_ulong)
				,KERNEL_NAME_VAR(integral_scan_h,_ulong)
				,KERNEL_NAME_VAR(integral_combine_h,_ulong)
				,ALIGN);
	}
	gray_matrix_cl transpose()const;
	void display(bool original=true,const std::string& title="", const bool display_info=true, unsigned int *const XYZ=nullptr,
            const bool exit_on_anykey=false)const;
	virtual ~gray_matrix_cl()=default;
};

} /* namespace gdface */

#endif /* FACEDETECT_GRAY_MATRIX_CL_H_ */
